package com.fish.common.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.fish.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * Excel工具类
 * 基于EasyExcel封装的导入导出工具
 *
 * @author fish
 */
@Slf4j
public class ExcelUtil {

    /**
     * 导出Excel文件到HttpServletResponse
     *
     * @param response  响应对象
     * @param fileName  文件名（不含扩展名）
     * @param sheetName Sheet名称
     * @param clazz     数据类型
     * @param data      数据列表
     */
    public static <T> void exportExcel(HttpServletResponse response,
                                       String fileName,
                                       String sheetName,
                                       Class<T> clazz,
                                       List<T> data) {
        try {
            // 设置响应头
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            
            // 文件名编码
            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8)
                    .replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", 
                    "attachment;filename*=utf-8''" + encodedFileName + ".xlsx");

            // 写入Excel
            EasyExcel.write(response.getOutputStream(), clazz)
                    .autoCloseStream(Boolean.FALSE)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .sheet(sheetName)
                    .doWrite(data);

            log.info("导出Excel成功，文件名: {}, 数据量: {}", fileName, data.size());
        } catch (IOException e) {
            log.error("导出Excel失败", e);
            throw new BusinessException("导出Excel失败");
        }
    }

    /**
     * 导出Excel文件到HttpServletResponse（多Sheet）
     *
     * @param response  响应对象
     * @param fileName  文件名（不含扩展名）
     * @param sheetDataList Sheet数据列表
     */
    public static void exportExcelMultiSheet(HttpServletResponse response,
                                             String fileName,
                                             List<SheetData<?>> sheetDataList) {
        try {
            // 设置响应头
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            
            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8)
                    .replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", 
                    "attachment;filename*=utf-8''" + encodedFileName + ".xlsx");

            // 创建ExcelWriter
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
                    .autoCloseStream(Boolean.FALSE)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .build();

            // 写入多个Sheet
            for (int i = 0; i < sheetDataList.size(); i++) {
                SheetData<?> sheetData = sheetDataList.get(i);
                WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetData.getSheetName())
                        .head(sheetData.getClazz())
                        .build();
                excelWriter.write(sheetData.getData(), writeSheet);
            }

            excelWriter.finish();
            log.info("导出多Sheet Excel成功，文件名: {}, Sheet数量: {}", fileName, sheetDataList.size());
        } catch (IOException e) {
            log.error("导出多Sheet Excel失败", e);
            throw new BusinessException("导出Excel失败");
        }
    }

    /**
     * 导入Excel文件
     *
     * @param file  上传的文件
     * @param clazz 数据类型
     * @return 数据列表
     */
    public static <T> List<T> importExcel(MultipartFile file, Class<T> clazz) {
        try {
            InputStream inputStream = file.getInputStream();
            List<T> data = EasyExcel.read(inputStream)
                    .head(clazz)
                    .sheet()
                    .doReadSync();
            
            log.info("导入Excel成功，数据量: {}", data.size());
            return data;
        } catch (IOException e) {
            log.error("导入Excel失败", e);
            throw new BusinessException("导入Excel失败");
        }
    }

    /**
     * 导入Excel文件（带监听器）
     *
     * @param file     上传的文件
     * @param clazz    数据类型
     * @param listener 读取监听器
     */
    public static <T> void importExcelWithListener(MultipartFile file,
                                                   Class<T> clazz,
                                                   com.alibaba.excel.read.listener.ReadListener<T> listener) {
        try {
            InputStream inputStream = file.getInputStream();
            EasyExcel.read(inputStream, clazz, listener)
                    .sheet()
                    .doRead();
            
            log.info("导入Excel成功（使用监听器）");
        } catch (IOException e) {
            log.error("导入Excel失败", e);
            throw new BusinessException("导入Excel失败");
        }
    }

    /**
     * 生成Excel模板
     *
     * @param response  响应对象
     * @param fileName  文件名（不含扩展名）
     * @param sheetName Sheet名称
     * @param clazz     数据类型（用于生成表头）
     */
    public static <T> void exportTemplate(HttpServletResponse response,
                                          String fileName,
                                          String sheetName,
                                          Class<T> clazz) {
        try {
            // 设置响应头
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            
            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8)
                    .replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", 
                    "attachment;filename*=utf-8''" + encodedFileName + ".xlsx");

            // 写入空数据（只有表头）
            EasyExcel.write(response.getOutputStream(), clazz)
                    .autoCloseStream(Boolean.FALSE)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .sheet(sheetName)
                    .doWrite(List.of());

            log.info("生成Excel模板成功，文件名: {}", fileName);
        } catch (IOException e) {
            log.error("生成Excel模板失败", e);
            throw new BusinessException("生成Excel模板失败");
        }
    }

    /**
     * Sheet数据封装类
     */
    public static class SheetData<T> {
        private String sheetName;
        private Class<T> clazz;
        private List<T> data;

        public SheetData(String sheetName, Class<T> clazz, List<T> data) {
            this.sheetName = sheetName;
            this.clazz = clazz;
            this.data = data;
        }

        public String getSheetName() {
            return sheetName;
        }

        public Class<T> getClazz() {
            return clazz;
        }

        public List<T> getData() {
            return data;
        }
    }
}

